Lexer   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 98
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 70
dl 0
loc 98
ccs 57
cts 57
cp 1
rs 10
c 0
b 0
f 0
wmc 18

1 Function

Rating   Name   Duplication   Size   Complexity  
F lex 0 84 18
1 3
import Token, { TokenType } from './Token'
2
3 3
const OPERATORS = {
4
  '+': 'add',
5
  '-': 'sub',
6
  '*': 'mul',
7
  '/': 'div',
8
  '**': 'pow',
9
  '^': 'pow',
10
  '=': 'set',
11
  '==': 'equals',
12
  '<': 'isLessThan',
13
  '>': 'isGreaterThan',
14
}
15
16
/**
17
 * @class Lexer
18
 * @name Lexer
19
 */
20 3
export class Lexer {
21
  s: string
22
23
  /**
24
   * Initialize a lexer.
25
   */
26
  constructor(input: string) {
27 16
    this.s = input
28
  }
29
30
  /**
31
   * Yields tokens as they are lexed.
32
   *
33
   */
34
  *lex(): Generator<Token> {
35 14
    let onSpace = false
36 14
    let onInteger = false
37 14
    let onWord = false
38 14
    let onOperator = false
39 14
    let buffer = ''
40
41
    // for each character
42 14
    for (let i = 0; i < this.s.length; i++) {
43 59
      const char = this.s[i] ?? '?'
44
45
      // if a space, combine with subsequent spaces into a separator
46 59
      if (/^\s$/.exec(char)) {
47 12
        if (onInteger) {
48 4
          yield new Token(TokenType.literal, buffer)
49 4
          onInteger = false
50 4
          buffer = ''
51 8
        } else if (onWord) {
52 2
          yield new Token(TokenType.identifier, buffer)
53 2
          onWord = false
54 2
          buffer = ''
55
        }
56 12
        onSpace = true
57 12
        buffer += char
58 12
        continue
59 47
      } else if (onSpace) {
60 7
        yield new Token(TokenType.separator, '')
61 7
        onSpace = false
62 7
        buffer = ''
63
      }
64
65
      // if a digit, combine with subsequent digits into a literal
66 47
      if (/^\d$/.exec(char)) {
67 14
        if (!onWord) {
68 11
          onInteger = true
69
        }
70 14
        buffer += char
71 14
        continue
72 33
      } else if (onInteger) {
73 1
        yield new Token(TokenType.literal, buffer)
74 1
        onInteger = false
75 1
        buffer = ''
76
      }
77
78
      // if a letter, combine with subsequent letters/numbers into an identifier
79 33
      if (/^[a-z]$/.exec(char) || (onWord && /^\d$/.exec(char))) {
80 26
        onWord = true
81 26
        buffer += char
82 26
        continue
83 7
      } else if (onWord) {
84 1
        yield new Token(TokenType.identifier, buffer)
85 1
        onWord = false
86 1
        buffer = ''
87
      }
88
89
      // if another char, combine with subsequent char and match an operator
90 7
      buffer += char
91 7
      if (buffer in OPERATORS) {
92 3
        yield new Token(TokenType.operator, buffer)
93 3
        onOperator = false
94 3
        buffer = ''
95 3
        continue
96
      }
97 4
      if (onOperator) {
98 2
        throw `Invalid operator "${buffer}"`
99
      }
100 2
      onOperator = true
101
    }
102
103 12
    if (onSpace) {
104 3
      yield new Token(TokenType.separator, '')
105
    }
106
107 12
    if (onInteger) {
108 3
      yield new Token(TokenType.literal, buffer)
109
    }
110
111 12
    if (onWord) {
112 6
      yield new Token(TokenType.identifier, buffer)
113
    }
114
  }
115
}
116
117
export default Lexer
118